package daisy;

import grafico.GeraHTML;
import grafico.GerenteGUI;

import java.util.ArrayList;
import java.util.List;

import main.Contexto;

import org.w3c.dom.Element;
import org.w3c.dom.NodeList;


public class LivroDTB extends EstruturaBase 
{
	static private LivroDTB instancia = null;

	private String nomeArqNCX;
	private List<RegBookRef> lisArqXML;
	private List<RegBookRef> lisArqSMIL;

	private RegNavPoint arvNCX;
	private List<RegPAR> lisTodosPAR;
	
	private EstruturaXML estXML;
	
	private int atual;
	
	private String caminhoLivro;

	private LivroDTB()
	{
		atual = 0;
		
		caminhoLivro = "";
		
		setNomeArquivo(null);
		setNomeArqNCX(null);
		setLisArqSMIL( new ArrayList<RegBookRef>() );
		setLisArqXML( new ArrayList<RegBookRef>() );

		arvNCX = null;
		lisTodosPAR = new ArrayList<RegPAR>();
		
		estXML = new EstruturaXML();
	}

	static public LivroDTB instancia()
	{
		if ( instancia == null )
			instancia = new LivroDTB();
		
		return instancia;
	}

	public String getNomeArqNCX()				  { return nomeArqNCX;		}
	public void setNomeArqNCX(String n)			  { nomeArqNCX = n; 		}
	public List<RegBookRef> getLisArqSMIL()		  { return lisArqSMIL; 		}
	public void setLisArqSMIL(List<RegBookRef> l) { lisArqSMIL = l;			}
	public List<RegBookRef> getLisArqXML()		  { return lisArqXML;		}
	public void setLisArqXML(List<RegBookRef> l)  { lisArqXML = l;			}
	public List<RegPAR> getLisTodosPAR()		  { return lisTodosPAR;		}
	public void setLisTodosPAR(List<RegPAR> l)    { lisTodosPAR = l;		}
	public void addLisTodosPAR(List<RegPAR> l)    { lisTodosPAR.addAll(l);	}
	public RegNavPoint getArvNCX()				  { return arvNCX;			}
	public void setArvNCX(RegNavPoint arv)		  {	arvNCX = arv;			}
	public EstruturaXML getEstXML()				  {	return estXML;			}
	public void setEstXML(EstruturaXML estXML)	  {	this.estXML = estXML;	}
	
	public boolean estaAberto()
	{
		return nomeArquivo != null;
	}

	public boolean abrir( String nomeArq )
	{
		Contexto contexto = Contexto.instancia();
		
		setNomeArquivo(nomeArq);
		
		caminhoLivro = contexto.extraiCaminho(nomeArq);

		System.out.println("Vou chamar ParseFile");
		
		if ( !parseFile(nomeArq) )
			return false;

		System.out.println("Vou carregar NomeArqs");
		
		if ( !carregaNomeArqs() )
			return false;

		System.out.println("Vou carregar Arquivos");
		
		return carregaArquivos();
	}
	
	/*
	 * Estrutura do arquivo OPF
	 * <package>
	 *     <manifest>
	 *         <item >
	 *     <spine>
	 *         <itemref>
	 */
	private boolean carregaNomeArqs()
	{
		Element elem;
		RegBookRef reg;
		String idManifest, href, mediaType;
		Element packageElem;
		NodeList lstItem;
		NodeList lstItemRef;
		
		// Elemento <package>
		packageElem = dom.getDocumentElement();
		
		lstItemRef = packageElem.getElementsByTagName("itemref");
		
		// Arquivo invÃ¡lido, sem itemref e sem spine
		if ( lstItemRef == null || lstItemRef.getLength() <= 0 )
		{
			System.err.println("Arquivo inválido, sem spine/itemref!");
			return false;
		}
		
		for( int i = 0 ; i < lstItemRef.getLength(); i++ ) 
		{
			elem = (Element)lstItemRef.item(i);
			
			// Verificar depois se o cara já está na lista e informar erro...
			reg = new RegBookRef( elem.getAttribute("idref") );
			lisArqSMIL.add(reg);
		}
		
		lstItem = packageElem.getElementsByTagName("item");
		
		// Arquivo inválido, sem item e sem manifest
		if ( lstItem == null || lstItem.getLength() <= 0 )
		{
			System.err.println("Arquivo inválido, sem manifest/item!");
			return false;
		}
		
		for( int i = 0 ; i < lstItem.getLength(); i++ ) 
		{
			elem = (Element)lstItem.item(i);
			
			href = elem.getAttribute("href");
			idManifest = elem.getAttribute("id");
			mediaType  = elem.getAttribute("media-type");
			
			if ( mediaType.equals("application/x-dtbncx+xml") )
			{
				if ( getNomeArqNCX() != null )
				{
					System.err.println("Mais de um arquivo NCX!");
					return false;
				}
				
				setNomeArqNCX( href );
			}
			else if ( mediaType.equals("application/x-dtbook+xml") )
			{
				reg = new RegBookRef( idManifest );
				reg.setNome(href);
				lisArqXML.add(reg);
			}
			else if ( mediaType.equals("application/smil") )
			{
				reg = buscaSmilPorId(idManifest);
				if ( reg == null )
				{
					System.err.println("Smil " + idManifest + " não encontrado no spine!");
					return false;
				}
				reg.setNome(href);
			}
		}

		if ( getNomeArqNCX() == null )
		{
			System.err.println("Livro inválido, sem arquivo NCX!");
			return false;
		}

		if ( lisArqSMIL.size() == 0 )
		{
			System.err.println("Livro inválido, sem arquivo SMIL!");
			return false;
		}
		
		return true;
	}
	
	private RegBookRef buscaSmilPorId(String id)
	{
		RegBookRef reg = null;
		
		for ( int i = 0; i < lisArqSMIL.size(); i++ )
		{
			reg = lisArqSMIL.get(i);
			if ( reg.getId().equals(id) )
				return reg;
		}
			
		return reg;
	}
	
	private boolean carregaArquivos()
	{
		RegBookRef reg;
		EstruturaNCX estNCX;
		EstruturaSMIL estSMIL;

		/*
		 * Carregar todos os SMIL
		 */
		for ( int i = 0; i < lisArqSMIL.size(); i++ )
		{
			reg = lisArqSMIL.get(i);
			
			estSMIL = new EstruturaSMIL();

			System.out.println("Vou abrir SMIL " + reg.getNome());
			
			/*
			 * Passar o reg como parâmetro para seus RegPAR apontarem para ele.
			 */
			if ( estSMIL.abrir(caminhoLivro, reg) )
			{
				addLisTodosPAR(estSMIL.getLstRegPAR());
				reg.addLstPAR(estSMIL.getLstRegPAR());
			}
		}

		System.out.println("Vou abrir NCX " + nomeArqNCX);
		
		/*
		 * Carrega o NCX apontando para o SMIL
		 */
		estNCX = new EstruturaNCX();
		if ( !estNCX.abrir(caminhoLivro + nomeArqNCX) )
		{
			System.err.println("Erro ao abrir arquivo " + nomeArqNCX + "!");
			return false;
		}
		
		System.out.println("Fim de carregar arquivos");
		
		arvNCX = estNCX.getNavMap();
		
		return true;
	}
	
	public RegPAR getRegPAR( String smilRef )
	{
		Referencia ref;
		RegBookRef reg;
		
		ref = new Referencia(smilRef);

		for ( int i = 0; i < lisArqSMIL.size(); i++ )
		{
			reg = lisArqSMIL.get(i);
			if ( reg.getNome().equals(ref.getNomeArq()) )
				return reg.getRegPAR(ref.getRefArq());
		}
		
		return null;
	}
	
	public void inicioTexto()
	{
		atual = 0;
	}
	
	private RegParteTexto getParte( int p )
	{
		RegPAR rPar;
		RegParteTexto parte;

		rPar = lisTodosPAR.get( p );
		
		if ( !estXML.estaCarregado(caminhoLivro + rPar.getNomeArqTexto()) )
		{
			if ( !estXML.abrir(caminhoLivro + rPar.getNomeArqTexto()) )
			{
				GerenteGUI.instancia().dlgAlerta( "Não pude abrir xml " + 
												  caminhoLivro + 
												  rPar.getNomeArqTexto() );
				return null;
			}
		}

		// Atual é o próximo RegPAR do arquivo Smil (que referencia o XML)
		parte = estXML.getSentenca(rPar.getRefTexto());
		
		if ( parte == null )
			GerenteGUI.instancia().dlgAlerta( "Não achei sentenca " + rPar.getTexto() );
		
		return parte;
	}
	
	public String proximoTexto()
	{
		int ordem;
		String texto = "";
		RegParteTexto parte;

		System.out.println("Atual = " + atual);

		// Fim do Livro
		if ( atual >= lisTodosPAR.size() )
			return null;
		
		ordem = -1;
		for ( int i = atual; i < lisTodosPAR.size(); i++ )
		{
			// Atual é o próximo RegPAR do arquivo Smil (que referencia o XML)
			if ( (parte = getParte( i )) == null )
				return null;
			
			if ( ordem == -1 )
				ordem = parte.getOrdem();

			if ( ordem != parte.getOrdem() )
				break;

			if ( parte.getTipo() == RegParteTexto.TXT_CABEC )
			{
				System.out.println("Atual(" + atual + ") -> " + parte.getTexto());

				atual++;

				System.out.println("Proximo(" + atual + ")");

				return GeraHTML.instancia().geraHn(parte.getNivel(), parte.getTexto());
			}

			System.out.println("Atual(" + atual + ") -> " + parte.getTexto());
			
			atual++;

			System.out.println("Proximo(" + atual + ")");
			
			texto += parte.getTexto();
		}

		return GeraHTML.instancia().geraP(texto);
	}
	
	public String montaEstruturaHTML()
	{
		return EstruturaNCX.montaListaHTML(getArvNCX());
	}
	
	public void voltaUmParagrafo()
	{
		if ( atual > 0 )
		{
			int ordem;
			RegParteTexto parte;

			System.out.println("Voltar - atual = " + atual);

			// Atual é o próximo RegPAR do arquivo Smil (que referencia o XML)
			if ( (parte = getParte( --atual )) == null )
				return;

			ordem = parte.getOrdem();

			System.out.println( "Voltar Inicio - atual = " + atual + " - Ordem = " + ordem + 
				" - Texto = " + parte.getTexto());
			
			while ( atual > 0)
			{
				// Atual é o próximo RegPAR do arquivo Smil (que referencia o XML)
				if ( (parte = getParte( --atual )) == null )
					return;

				System.out.println( "Voltar laco1 - atual = " + atual + 
						" - Ordem = " + parte.getOrdem() + 
						" - Texto = " + parte.getTexto());

				if ( ordem != parte.getOrdem() )
					break;
			}
			
			System.out.println( "Voltar laco1 Sai - atual = " + atual);

			if ( atual == 0 )
				return;
			
			ordem = parte.getOrdem();

			System.out.println( "Voltar laco2 Ordem = " + ordem);

			while ( atual > 0)
			{
				// Atual é o próximo RegPAR do arquivo Smil (que referencia o XML)
				if ( (parte = getParte( --atual )) == null )
					return;

				System.out.println( "Voltar laco2 - atual = " + atual + 
						" - Ordem = " + parte.getOrdem() + 
						" - Texto = " + parte.getTexto());

				if ( ordem != parte.getOrdem() )
				{
					atual++;
					break;
				}
			}

			System.out.println( "Voltar laco2 Sai - atual = " + atual);
		}
	}
	
	public void voltaInicioParagrafo()
	{
		if ( atual > 0 )
		{
			int ordem;
			RegParteTexto parte;

			System.out.println("Voltar - atual = " + atual);

			// Atual é o próximo RegPAR do arquivo Smil (que referencia o XML)
			if ( (parte = getParte( --atual )) == null )
				return;

			ordem = parte.getOrdem();

			System.out.println( "Voltar Inicio - atual = " + atual + " - Ordem = " + ordem + 
				" - Texto = " + parte.getTexto());
			
			while ( atual > 0)
			{
				// Atual é o próximo RegPAR do arquivo Smil (que referencia o XML)
				if ( (parte = getParte( --atual )) == null )
					return;

				System.out.println( "Voltar laco1 - atual = " + atual + 
						" - Ordem = " + parte.getOrdem() + 
						" - Texto = " + parte.getTexto());

				if ( ordem != parte.getOrdem() )
				{
					atual++;
					break;
				}
			}
			
			System.out.println( "Voltar laco1 Sai - atual = " + atual);
		}
	}
	
	public void avancaUmaSentenca()
	{
		if ( atual < lisTodosPAR.size() )
			atual++;
	}
}
